package com.mobilesales.android.infrastructure;

import java.util.List;

import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;

import com.mobilesales.android.database.ClientManager;
import com.mobilesales.android.database.DatabaseHelper;
import com.mobilesales.android.database.EmployeeManager;
import com.mobilesales.android.database.OrderManager;
import com.mobilesales.android.database.ParamManager;
import com.mobilesales.android.database.ProductManager;
import com.mobilesales.android.database.TaskManager;
import com.mobilesales.android.model.clients.Client;
import com.mobilesales.android.model.clients.ClientGroup;
import com.mobilesales.android.model.employees.Employee;
import com.mobilesales.android.model.employees.Message;
import com.mobilesales.android.model.orders.Order;
import com.mobilesales.android.model.orders.OrderStatus;
import com.mobilesales.android.model.params.Param;
import com.mobilesales.android.model.params.ParamType;
import com.mobilesales.android.model.products.Category;
import com.mobilesales.android.model.products.Product;
import com.mobilesales.android.model.products.Tax;
import com.mobilesales.android.model.products.Unit;
import com.mobilesales.android.model.tasks.CyclicTask;
import com.mobilesales.android.model.tasks.Schedule;
import com.mobilesales.android.model.tasks.Task;
import com.mobilesales.android.model.tasks.TaskStatus;
import com.mobilesales.android.model.tasks.TaskType;
import com.mobilesales.android.services.ClientServiceClient;
import com.mobilesales.android.services.EmployeeServiceClient;
import com.mobilesales.android.services.OrderServiceClient;
import com.mobilesales.android.services.ParamServiceClient;
import com.mobilesales.android.services.ProductServiceClient;
import com.mobilesales.android.services.TaskServiceClient;
import com.mobilesales.android.util.LogFlags;
import com.mobilesales.android.util.Settings;

public class SyncService {

	private Context context;
	protected SQLiteDatabase db;
	protected DatabaseHelper dbHelper;
	private IProgressUpdater task;
	
	private int currentStep = 1;
	private final int steps = 15;
	private final int maxValue = 100;

	public SyncService(Context ctx, IProgressUpdater task) {
		this.context = ctx;
		this.task = task;
		this.currentStep = 1;
	}

	private void clearTables(String... tableNames) {	
		if (tableNames!=null) {
			for (String tab : tableNames) {
				db.delete(tab, null, null);
			}
		}
	}
	
	private void clearCyclicTaskTable() {
		db.delete("Tasks", "ScheduleId is null", null);
	}
	
	private void clearTaskTable() {
		db.delete("Tasks", "ScheduleId is not null", null);
	}
	
	private void updateProgress() {
		int progress = (int)(currentStep/(steps*1.0)*100);
		if (currentStep==steps) progress = maxValue;
		currentStep++;
		task.onProgressUpdated(progress);
	}

	public boolean synchronize() {
		if (Settings.getSettings() == null)
			return false;
		boolean success = true;

		dbHelper = new DatabaseHelper(this.context);
		db = dbHelper.getDatabase();
		try {
			this.synchronizeClientGroups();
			this.updateProgress();
			this.synchronizeParams();
			this.updateProgress();
			this.synchronizeEmployees();
			this.updateProgress();			
			this.synchronizeTaskTypes();
			this.updateProgress();			
			this.synchronizeTaskStatuses();
			this.updateProgress();			
			this.synchronizeCategories();
			this.updateProgress();
			this.synchronizeOrderStatuses();
			this.updateProgress();			
			this.synchronizeTaxes();
			this.updateProgress();
			this.synchronizeUnits();
			this.updateProgress();
			this.synchronizeClients();
			this.updateProgress();
			this.synchronizeMessages();
			this.updateProgress();
			this.synchronizeProducts();
			this.updateProgress();
			this.synchronizeTasks();
			this.updateProgress();
			this.synchronizeCyclicTasks();
			this.updateProgress();
			this.synchronizeOrders();
			this.updateProgress();
		} catch (Exception ex) {
			Log.e(LogFlags.RestService, ex.getMessage(), ex);
			success = false;
		}
		dbHelper.close();
		return success;
	}

	private void synchronizeUnits() throws Exception {
		ProductServiceClient productService = new ProductServiceClient(
				Settings.ProductService());
		ProductManager manager = new ProductManager(context);
		List<Unit> items = productService.GetUnitList();
		boolean success = true;
		if (items != null) {
			this.clearTables("Units");
			for (Unit u : items) {
				boolean localSuccess = manager.StoreUnit(u);
				success &= localSuccess;
			}
		}
		manager.Close();
		if (!success)
			throw new Exception("Could not synchronize units");
		
	}

	private void synchronizeTaxes() throws Exception {
		ProductServiceClient productService = new ProductServiceClient(
				Settings.ProductService());
		ProductManager manager = new ProductManager(context);
		List<Tax> items = productService.GetTaxList();
		boolean success = true;
		if (items != null) {
			this.clearTables("Taxes");
			for (Tax t : items) {
				boolean localSuccess = manager.StoreTax(t);
				success &= localSuccess;
			}
		}
		manager.Close();
		if (!success)
			throw new Exception("Could not synchronize taxes");
	}

	private void synchronizeOrders() throws Exception {
		OrderServiceClient orderService = new OrderServiceClient(
				Settings.OrderService());
		OrderManager orderManager = new OrderManager(context);
		List<Order> orders = orderService.GetOrderList();
		boolean success = true;
		if (orders != null) {
			this.clearTables("Orders","OrderElements");
			for (Order o : orders) {
				boolean localSuccess = orderManager.StoreOrder(o);
				success &= localSuccess;
			}
		} else {
			success = false;
		}
		orderManager.Close();
		if (!success)
			throw new Exception("Could not synchronize orders");
	}

	private void synchronizeOrderStatuses() throws Exception {

		OrderServiceClient orderService = new OrderServiceClient(
				Settings.OrderService());
		OrderManager orderManager = new OrderManager(context);
		List<OrderStatus> statuses = orderService.GetOrderStatusList();
		boolean success = true;
		if (statuses != null) {
			this.clearTables("OrderStatus");
			for (OrderStatus s : statuses) {
				boolean localSuccess = orderManager.StoreOrderStatus(s);
				success &= localSuccess;
			}
		} else {
			success = false;
		}
		orderManager.Close();
		if (!success)
			throw new Exception("Could not synchronize order statuses");

	}

	private void synchronizeProducts() throws Exception {

		ProductServiceClient productService = new ProductServiceClient(
				Settings.ProductService());
		ProductManager manager = new ProductManager(context);
		List<Product> items = productService.GetProductList();
		boolean success = true;
		if (items != null) {
			this.clearTables("Products","ProductHasParams","ProductImage","ProductHasCategories");
			for (Product p : items) {
				boolean localSuccess = manager.StoreProduct(p);
				success &= localSuccess;
			}
		}
		manager.Close();
		if (!success)
			throw new Exception("Could not synchronize Products");
	}

	private void synchronizeCategories() throws Exception {
		ProductServiceClient productService = new ProductServiceClient(
				Settings.ProductService());
		ProductManager manager = new ProductManager(context);
		List<Category> items = productService.GetCategoryList();
		boolean success = true;
		if (items != null) {
			this.clearTables("Categories");
			for (Category c : items) {
				boolean localSuccess = manager.StoreCategory(c);
				success &= localSuccess;
			}
		}
		manager.Close();
		if (!success)
			throw new Exception("Could not synchronize Categories");
	}

	private void synchronizeCyclicTasks() throws Exception {
		int employeeId = Settings.getSettings().getEmployeeId();
		TaskServiceClient taskService = new TaskServiceClient(
				Settings.TaskService());
		TaskManager taskManager = new TaskManager(context);
		List<CyclicTask> items = taskService.getCyclicTaskList(employeeId);
		boolean success = true;
		if (items != null) {
			this.clearTables("EmployeeHasCyclicTasks");
			this.clearCyclicTaskTable();
			for (CyclicTask t : items) {
				boolean localSuccess = taskManager.StoreCyclicTask(t);
				success &= localSuccess;
			}

		}
		taskManager.Close();
		if (!success)
			throw new Exception("Could not synchronize Cyclic Tasks");
	}

	private void synchronizeTasks() throws Exception {
		int employeeId = Settings.getSettings().getEmployeeId();
		TaskServiceClient taskService = new TaskServiceClient(
				Settings.TaskService());
		TaskManager taskManager = new TaskManager(context);
		List<Schedule> items = taskService.getScheduleList(employeeId);
		boolean success = true;
		if (items != null) {
			this.clearTables("Schedules");
			this.clearTaskTable();
			for (Schedule s : items) {
				boolean localSuccess = taskManager.StoreSchedule(s);
				success &= localSuccess;
				Log.i("zxc", "afterStore");
				if (localSuccess) {
					List<Task> tasks = taskService.getTaskList(s.Id);
					if (tasks != null) {
						for (Task t : tasks) {
							localSuccess = taskManager.StoreTask(t);
							success &= localSuccess;
						}
					} else
						success = false;
				}
			}
		}
		taskManager.Close();
		if (!success)
			throw new Exception("Could not synchronize Tasks");

	}

	private void synchronizeTaskStatuses() throws Exception {
		TaskServiceClient taskService = new TaskServiceClient(
				Settings.TaskService());
		TaskManager taskManager = new TaskManager(context);
		List<TaskStatus> items = taskService.getTaskStatusList();
		boolean success = true;
		if (items != null) {
			this.clearTables("TaskStatus");
			for (TaskStatus t : items) {
				boolean localSuccess = taskManager.StoreTaskStatus(t);
				success &= localSuccess;
			}

		}
		taskManager.Close();
		if (!success)
			throw new Exception("Could not synchronize Task Statuses");

	}

	private void synchronizeTaskTypes() throws Exception {
		TaskServiceClient taskService = new TaskServiceClient(
				Settings.TaskService());
		TaskManager taskManager = new TaskManager(context);
		List<TaskType> items = taskService.getTaskTypeList();
		boolean success = true;
		if (items != null) {
			this.clearTables("TaskTypes");
			for (TaskType t : items) {
				boolean localSuccess = taskManager.StoreTaskType(t);
				success &= localSuccess;
			}

		}
		taskManager.Close();
		if (!success)
			throw new Exception("Could not synchronize Task Types");

	}

	private void synchronizeMessages() throws Exception {
		int employeeId = Settings.getSettings().getEmployeeId();
		EmployeeServiceClient employeeService = new EmployeeServiceClient(
				Settings.EmployeeService());
		EmployeeManager employeeManager = new EmployeeManager(context);
		List<Message> messages = employeeService.GetMessageList(employeeId);
		boolean success = true;
		if (messages != null) {
			this.clearTables("Messages");
			for (Message m : messages) {
				boolean localSuccess = employeeManager.StoreMessage(m);
				success &= localSuccess;
			}

		}
		employeeManager.Close();
		if (!success)
			throw new Exception("Could not synchronize Messages");
	}

	private void synchronizeEmployees() throws Exception {
		EmployeeServiceClient employeeService = new EmployeeServiceClient(
				Settings.EmployeeService());
		EmployeeManager employeeManager = new EmployeeManager(context);
		List<Employee> employees = employeeService.GetEmployeeList();
		boolean success = true;
		if (employees != null) {
			this.clearTables("Employees");
			for (Employee e : employees) {
				boolean localSuccess = employeeManager.StoreEmployee(e);
				success &= localSuccess;
			}

		}
		employeeManager.Close();
		if (!success)
			throw new Exception("Could not synchronize Employees");
	}

	private void synchronizeParams() throws Exception {
		ParamServiceClient paramService = new ParamServiceClient(
				Settings.ParamService());
		ParamManager paramManager = new ParamManager(context);
		List<Param> params = paramService.getParamList();
		boolean success = true;
		if (params != null) {
			this.clearTables("Params");
			for (Param p : params) {
				if (p.ParamType != ParamType.Employee) {
					boolean localSuccess = paramManager.StoreParam(p);
					success &= localSuccess;
				}
			}
		} else {
			success = false;
		}
		paramManager.Close();
		if (!success)
			throw new Exception("Could not synchronize params");
	}

	private void synchronizeClientGroups() throws Exception {
		ClientServiceClient clientService = new ClientServiceClient(
				Settings.ClientService());
		ClientManager clientManager = new ClientManager(context);
		List<ClientGroup> groups = clientService.getClientGroupList();
		boolean success = true;
		if (groups != null) {
			this.clearTables("ClientGroups");
			for (ClientGroup g : groups) {
				boolean localSuccess = clientManager.StoreClientGroup(g);
				success &= localSuccess;
			}
		} else {
			success = false;
		}
		clientManager.Close();
		if (!success)
			throw new Exception("Could not synchronize client groups");
	}

	private void synchronizeClients() throws Exception {
		ClientServiceClient clientService = new ClientServiceClient(
				Settings.ClientService());
		List<Client> clients = clientService.getClientList();
		ClientManager clientManager = new ClientManager(context);
		boolean success = true;
		if (clients != null) {
			this.clearTables("ClientHasParams","Clients");
			for (Client c : clients) {
				boolean localSuccess = clientManager.StoreClient(c);
				success &= localSuccess;
			}

		} else {
			success = false;
		}
		clientManager.Close();
		if (!success)
			throw new Exception("Could not synchronize clients");
	}

}
